package com.qitianyong.decoder;

import android.media.MediaCodec;
import android.media.MediaFormat;
import android.util.Log;
import android.view.Surface;

import com.ligo.log.util.ZyLog;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by qitianyong on 2017/3/16.
 * 硬解码
 */

public class HardDecoder {

    private final static String MIME_TYPE = "video/avc";
    private final int width;
    private final int height;
    private final Surface surface;
    private MediaCodec __Codec;
    private Lock __lock = new ReentrantLock();
    private int __count = 0;
    private  MediaFormat mediaFormat;
    private  byte[] csd0;
    private  byte[] csd1;

    public HardDecoder(int width, int height, Surface surface){
        this.width = width;
        this.height = height;
        this.surface = surface;
        try {
            __Codec = MediaCodec.createDecoderByType(MIME_TYPE);
        } catch (IOException e) {
            e.printStackTrace();
        }

        if(__Codec != null) {
            mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE,
                    width, height);
            __Codec.configure(mediaFormat, surface, null, 0);
            __Codec.start();

//            Log.i("mycodec", "codec ok");
            ZyLog.d("codec ok");
        }else{
            ZyLog.d("codec err");
//            Log.i("mycodec", "codec err");
        }
    }
    private void reInitCode(){
        __lock.lock();
        try {
            __Codec = MediaCodec.createDecoderByType(MIME_TYPE);
            if(mediaFormat == null){
                mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE,
                        width, height);
            }
            System.out.println("reInitCode");
            if(csd0!=null&&csd1!=null){
               mediaFormat.setByteBuffer("csd-0",ByteBuffer.wrap(csd0));
              mediaFormat.setByteBuffer("csd-1",ByteBuffer.wrap(csd1));
            }
            __Codec.configure(mediaFormat, surface, null, 0);
            __Codec.start();
        } catch (IOException q) {
            q.printStackTrace();
        }
        __lock.unlock();
    }

    public void Destroy(){

        __lock.lock();
        if(__Codec != null){
            __Codec.stop();
            __Codec.release();
            __Codec = null;

            Log.i("mycodec", "release codec ok");
        }
        __lock.unlock();
    }

    private  void initCsd(byte[] data){
        int csd0Start = 0,csd0End = 0,csd1Start = 0,csd1End = 0;
        for (int i = 0; i < data.length-5; i++) {
            if(data[i] == 0x00&&data[i+1]==0x00&&data[i+2]==0x00&&data[i+3]==0x01&&data[i+4]==0x47){
                csd0Start = i;
                continue;
            }
            if(data[i] == 0x00&&data[i+1]==0x00&&data[i+2]==0x00&&data[i+3]==0x01&&data[i+4]==0x48){
                csd0End = i;
                csd1Start = i;
                continue;
            }
            if(data[i] == 0x00&&data[i+1]==0x00&&data[i+2]==0x00&&data[i+3]==0x01&&csd0End!=0){
                csd1End = i;
                break;
            }
        }
        try {
            csd0 = Arrays.copyOfRange(data,csd0Start,csd0End);
            csd1 = Arrays.copyOfRange(data,csd1Start,csd1End);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public boolean Decoder(byte[] data, int length){
        if (csd0 == null &&data.length>5) {
            ZyLog.d("data[4]=" + data[4]);
//            if (data[4] == 0x47 || data[4] == 0x61||data[4]==0x67) {
                initCsd(data);
//            }
        }
        if(csd0 == null){
            ZyLog.d("csd0 is null");
            return false;
        }
        __lock.lock();
        try {
        if (__Codec != null) {
            ByteBuffer[] inputBuffers = __Codec.getInputBuffers();
            int inputBufferIndex = __Codec.dequeueInputBuffer(-1);
            if (inputBufferIndex >= 0) {
                ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                inputBuffer.clear();
                inputBuffer.put(data, 0, length);
                __Codec.queueInputBuffer(inputBufferIndex, 0, length, __count * 200, 0);
                __count++;

                //Log.i("mcodec", "dequeueInputBuffer ok");
            } else {
                __lock.unlock();
                ZyLog.d("dequeueInputBuffer error");
                //Log.i("mycodec", "dequeueInputBuffer err");
                return false;
            }

            // Get output buffer index
            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            int outputBufferIndex = __Codec.dequeueOutputBuffer(bufferInfo, 100);
            while (outputBufferIndex >= 0) {
                __Codec.releaseOutputBuffer(outputBufferIndex, true);
                outputBufferIndex = __Codec.dequeueOutputBuffer(bufferInfo, 0);
            }
        }
        } catch (Exception e) {
            e.printStackTrace();
            ZyLog.d("Exception error " + e.getMessage());
            __lock.unlock();
            Destroy();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    reInitCode();
                }
            }).start();
            return false ;
        }

        __lock.unlock();
        return true;
    }
}
